<?php

namespace App\Console\Commands;

use App\Service\Api\QrCodeService;
use chillerlan\QRCode\QRCode;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
use Carbon\Carbon;
use Zxing\QrReader;

class UpdateQrCodeExpireTime extends Command
{
    // 命令的名字和描述
    protected $signature   = 'qrcode:update-expire-time  {--name=}';
    protected $description = '批量更新二维码过期时间为当前时间向后加10天，并更新sign参数到token中';
    protected $storeName   = 0;

    public function __construct()
    {
        parent::__construct();
    }

    public function handle()
    {
        $this->storeName = $this->option('name', 0);

        $newExpireTime = Carbon::now()->addDays(10)->endOfDay()->timestamp;

        // 获取需要更新的二维码总数
        $total = DB::table('sx_sales_qr_codes')->where('token', '')->count();

        if ($total == 0) {
            $this->info('没有需要更新的二维码。');

            return;
        }
        // $domain = 'http://osstest.sx1211.com/';
        $domain = 'https://img.sx1211.com/';
        $this->output->progressStart($total);

        // 分批处理
        DB::table('sx_sales_qr_codes')
          ->where('token', '')
          ->orderBy('id')
          ->chunkById(100, function ($qrCodes) use ($newExpireTime, $domain) {
              foreach ( $qrCodes as $qrCode ) {
                  // 使用 Redis 锁，锁定处理该二维码的记录
                  $lockKey = 'qrcode:lock:'.$qrCode->id;
                  if (Redis::set($lockKey, 1, 'NX', 'EX', 300)) { // 锁的有效时间为 300 秒，避免死锁
                      try {
                          $imgUrl = $domain.$qrCode->qr_code_uri;
                          $qrCodeFile = $this->downloadQrCode($imgUrl, $qrCode->id);
                          $qrReader = new QrReader($qrCodeFile);
                          $qrText = $qrReader->text();

                          if (! $qrText) {
                              $uploadUrl = $this->getUploadUrl($qrCodeFile);
                              if (! $uploadUrl) {
                                  $this->error("二维码读取失败，ID: {$qrCode->id}, 上传本地文件失败");
                                  continue;
                              }
                              [$qrText, $errorMessage] = $this->getQrcodeByUrl($uploadUrl);
                              if (! $qrText) {
                                  $this->error("二维码读取失败，ID: {$qrCode->id}, 接口也没有获取到链接: ".$errorMessage);
                                  continue;
                              }
                          }

                          $sign = $this->extractSignFromUrl($qrText);
                          if (! $sign) {
                              $this->error("二维码读取失败，ID: {$qrCode->id}, 没有提取出来 token 信息");
                              continue;
                          }

                          // 更新记录的过期时间和 token (sign)
                          DB::table('sx_sales_qr_codes')
                            ->where('id', $qrCode->id)
                            ->update([
                                'expire_time' => $newExpireTime,
                                'token'       => $sign,
                            ]);

                          // 每更新一条，进度条前进一格
                          $this->output->progressAdvance();
                      } catch (\Throwable $e) {
                          $this->error("二维码读取失败，ID: {$qrCode->id}, 错误: ".$e->getMessage());
                      } finally {
                          // 释放锁
                          Redis::del($lockKey);
                      }
                  } else {
                      // 每更新一条，进度条前进一格
                      $this->output->progressAdvance();
                  }
              }
          });

        $this->output->progressFinish();
        $this->info('所有二维码过期时间和 token 更新完毕！');
    }

    /**
     * 下载二维码到本地
     *
     * @param string $uri
     * @return string
     */
    private function downloadQrCode($uri, $id = 0)
    {
        $contents = file_get_contents($uri);
        $path = storage_path('app/temp_qr_code_'.$this->storeName.'_'.rand(0, 10).'.jpg');
        file_put_contents($path, $contents);

        return $path;
    }

    /**
     * 获取二维码区域图片并增强图像对比度
     *
     * @param $filePath
     * @param $qrcodePath
     * @return mixed
     */
    public function getQrcodeImg($filePath, $qrcodePath)
    {
        $sourcePath = $filePath;
        try {
            $sourceImage = imagecreatefromjpeg($sourcePath);
        } catch (\Throwable $exception) {
            $sourceImage = imagecreatefrompng($sourcePath);
        }

        $x = 175;
        $y = 450;
        $width = 400;
        $height = 400;

        $croppedImage = imagecreatetruecolor($width, $height);
        imagecopy($croppedImage, $sourceImage, 0, 0, $x, $y, $width, $height);
        imagefilter($croppedImage, IMG_FILTER_CONTRAST, -15);

        imagejpeg($croppedImage, $qrcodePath);

        imagedestroy($sourceImage);
        imagedestroy($croppedImage);

        return $qrcodePath;
    }

    /**
     * 从URL中提取sign参数
     *
     * @param string $url
     * @return string|null
     */
    private function extractSignFromUrl($url)
    {
        $parsedUrl = parse_url($url);
        if (isset($parsedUrl['query'])) {
            parse_str($parsedUrl['query'], $queryParams);

            return $queryParams['sign'] ?? null;
        }

        return null;
    }

    /**
     * 获取要进行接口请求的图片地址，目前通过裁剪
     *
     * @param $qrCodeFile
     * @return mixed
     */
    public function getUploadUrl($qrCodeFile)
    {
        $path = 'upload/temp_qr_code_'.$this->storeName.'.jpg';
        $this->getQrcodeImg($qrCodeFile, public_path($path));

        return 'https://exam.frp.wowoxia.club/'.$path;
    }

    /**
     * 通过第三方接口获取二维码内容
     *
     * @param $url
     * @return mixed|string
     */
    public function getQrcodeByUrl($url)
    {
        $service = new QrCodeService();
        $result = $service->readQRCode($url);
        $linkUrl = '';
        if ($result['code'] == 1) {
            $linkUrl = $result['qrurl'];
        }

        return [$linkUrl, $result['msg'] ?? ''];
    }
}
